/**
 * @file  MiraZipper.h
 * @author Josep Sabater (jsabaterm@el.ub.edu)
 * @date   August, 2016
 * @version 1.0.0
 * @ingroup MiraUtilUBLib
 * @brief return a pair with the value and index of the current element in
 * an auto for loop.
 *
 *
 */
#ifndef MIRA_ZIPPER_H
#define MIRA_ZIPPER_H


#include <iterator>

namespace MiraUtilUBLib {

    template <typename T>
    struct iterator_extractor { typedef typename T::iterator type; };

    template <typename T>
    struct iterator_extractor<T const> { typedef typename T::const_iterator type; };


    template <typename T>
    class Zipper {
    public:
        class iterator {
            typedef typename iterator_extractor<T>::type inner_iterator;

            typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;
        public:
            typedef std::pair<size_t, inner_reference> reference;

            iterator(inner_iterator it): _pos(0), _it(it) {}

            reference operator*() const { return reference(_pos, *_it); }

            iterator& operator++() { ++_pos; ++_it; return *this; }
            iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }

            bool operator==(iterator const& it) const { return _it == it._it; }
            bool operator!=(iterator const& it) const { return !(*this == it); }

        private:
            size_t _pos;
            inner_iterator _it;
        };

        Zipper(T& t): _container(t) {}

        iterator begin() const { return iterator(_container.begin()); }
        iterator end() const { return iterator(_container.end()); }

    private:
        T& _container;
    }; // class Zipper

    template <typename T>
    Zipper<T> index(T& t) { return Zipper<T>(t); }
}

#endif
